<?php

abstract class Jf_Orm_Abstract {

    protected $_resource = null;

    protected $_resourceModel = null;

    protected $_counter = array();

    protected $_filters = null;

    protected $_index = null;

    protected $_enabledCache = false;

    protected $_readOnly = false;

    public static $_WRITE_METHOD = array('insert','save','update','updateByPrimary','batchInsert','delete','deleteByPrimary');

    protected function setResource($resource) {
        $this->_resource = $resource;
    }

    protected function getResource() {
        if (empty($this->_resource)) {
            throw new Jf_Exception(get_class($this) . ' Resource is not set ');
        }
        if (! isset($this->_resourceModel)) {
            $this->_resourceModel = Jf::getResource($this->_resource);
        }
        return $this->_resourceModel;
    }

    public function setFilters($filters) {
        $this->_filters = $filters;
        return $this;
    }

    public function getCounter($counterName) {
        $counterName = ucfirst($counterName);
        if (! isset($this->_counter[$counterName])) {
            $counter = 'get' . $counterName . 'Counter';
            $this->_counter[$counterName] = $this->$counter($this->_filters);
        }
        return $this->_counter[$counterName];
    }

    public function getPaginator($perPage, $page, $where = null, $attributes = '*', $order = null) {
        $offset = ($page > 0) ? ($page - 1) * $perPage : 0;
        $rows = $this->fetchAll($where, $attributes, $order, $perPage, $offset);
        $count = $this->totalCount($where);
        $result = new Jf_RecordSet($rows);
        $adapter = new Jf_Paginator_Adapter_Iterator($result, $count);
        $paginator = new Zend_Paginator($adapter);
        $paginator->setItemCountPerPage($perPage)->setCurrentPageNumber($page);
        return $paginator;
    }

    public function disableCache() {
        $this->_enabledCache = false;
        return $this;
    }

    public function enableCache() {
        $this->_enabledCache = true;
        return $this;
    }

    public function setReadOnly($flag = true) {
        $this->_readOnly = (bool) $flag;
        return $this;
    }

    public function __call($method, $arguments) {
        if ($this->_readOnly == true && in_array($method, self::$_WRITE_METHOD)) {
            throw new Jf_Exception('read only~~');
        }
        if (! $this->_enabledCache) {
            return call_user_func_array(array($this->getResource(),$method), $arguments);
        }
        switch ($method) {
            case 'fetchRow':
                $key = md5(get_class($this) . serialize($arguments));
                $data = Jf::cache()->get($key);
                if (false === $data) {
                    $data = call_user_func_array(array($this->getResource(),$method), $arguments);
                    Jf::cache()->set($key, $data);
                }
                break;
            case 'fetchOneByPrimary':
                $col = $arguments[0];
                $id = $arguments[1];
                $row = $this->findByPrimary($id);
                $data = isset($row[$col]) ? $row[$col] : null;
                break;
            case 'findByPrimary':
                $key = get_class($this) . '/' . $arguments[0];
                $data = Jf::cache()->get($key);
                if (false === $data) {
                    $data = call_user_func_array(array($this->getResource(),$method), $arguments);
                    Jf::cache()->set($key, $data);
                }
                break;
            case 'fetchAll':
                $key = md5(get_class($this) . serialize($arguments));
                $secondKeys = Jf::cache()->get($key);
                if (false === $secondKeys) {
                    $data = call_user_func_array(array($this->getResource(),$method), $arguments);
                    $secondKeys = array();
                    $secondData = array();
                    foreach ($data as $value) {
                        $secondKey = get_class($this) . '/' . current($value);
                        $secondKeys[] = $secondKey;
                        $secondData[$secondKey] = $value;
                    }
                    Jf::cache()->set($key, $secondKeys);
                    Jf::cache()->setMulti($secondData);
                } else {
                    $data = array_values(Jf::cache()->getMulti($secondKeys));
                }
                break;
            case 'totalCount':
                $key = md5(get_class($this) . serialize($arguments));
                $data = Jf::cache()->get($key);
                if (false === $data) {
                    $data = call_user_func_array(array($this->getResource(),$method), $arguments);
                    Jf::cache()->set($key, $data);
                }
                break;
            default:
                return call_user_func_array(array($this->getResource(),$method), $arguments);
        }
        return $data;
    }

    public function uniqHash($prefix = '') {
        return $prefix . md5(uniqid(microtime() . mt_rand(), true));
    }
}
